package com.mjk.common.core.prometheus.pushgetway;

import com.mjk.common.base.constns.BeeCommonConstns;
import com.mjk.common.core.prometheus.annotation.BeeMetric;
import com.mjk.common.tools.logger.BizLogger;
import com.mjk.common.util.ClassUtil;
import com.mjk.common.util.ComputerUtils;
import com.mjk.common.util.ReflectionUtils;
import io.prometheus.client.Collector;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Counter;
import io.prometheus.client.exporter.PushGateway;
import org.apache.dubbo.qos.api.BaseCommand;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

import javax.annotation.PreDestroy;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


/**
 * Configuration for
 * <a href="https://github.com/prometheus/pushgateway">Prometheus
 * Pushgateway</a>.
 *
 * @author David J. M. Karlsen
 */
@Configuration
@ConditionalOnProperty(name = "management.exposure.prometheus.pushgateway.enabled", havingValue = "true", matchIfMissing = false)
public class PrometheusPushGatewayConfiguration {
    private final CollectorRegistry collectorRegistry;
    private final PushgatewayProperties pushgatewayProperties;
    private final PushGateway pushGateway;
    private final Environment environment;
    private final ScheduledExecutorService executorService;

    PrometheusPushGatewayConfiguration(CollectorRegistry collectorRegistry,
                                       PushgatewayProperties pushgatewayProperties, Environment environment) {
        this.collectorRegistry = collectorRegistry;
        this.pushgatewayProperties = pushgatewayProperties;
        this.pushGateway = new PushGateway(pushgatewayProperties.getBaseUrl());
        this.environment = environment;
        this.executorService = Executors.newSingleThreadScheduledExecutor((r) -> {
            final Thread thread = new Thread(r);
            thread.setDaemon(true);
            thread.setName("micrometer-pushgateway");
            return thread;
        });
        executorService.scheduleAtFixedRate(this::push, 0, pushgatewayProperties.getPushRate().toMillis(),
                TimeUnit.MILLISECONDS);
        //注册自定义注解
        Set<Class<?>> clazzs = ClassUtil.scanPackageByAnnotation(BeeCommonConstns.BASE_PACKAGE,true, BeeMetric.class);
        for(Class<?> clazz : clazzs) {
            this.collectorRegistry.register((Collector) ReflectionUtils.newInstance(clazz));
        }
    }



    void push() {
        try {
            pushgatewayProperties.setGroupingKeys(Map.of("ip", ComputerUtils.getIpAddress(), "port", getPort()));
            pushGateway.pushAdd(collectorRegistry, job(), pushgatewayProperties.getGroupingKeys());
        } catch (UnknownHostException e) {
            BizLogger.error("Unable to locate host '" + pushgatewayProperties.getBaseUrl()
                    + "'. No longer attempting metrics publication to this host");
            executorService.shutdown();
        } catch (Throwable t) {
            BizLogger.error("Unable to push metrics to Prometheus Pushgateway", t);
        }
    }

    @PreDestroy
    void shutdown() {
        executorService.shutdown();
        if (pushgatewayProperties.isPushOnShutdown()) {
            push();
        }
        if (pushgatewayProperties.isDeleteOnShutdown()) {
            try {
                pushGateway.delete(job(), pushgatewayProperties.getGroupingKeys());
            } catch (Throwable t) {
                BizLogger.error("Unable to delete metrics from Prometheus Pushgateway", t);
            }
        }
    }

    /**
     * There's a history of Prometheus spring integration defaulting the job name to
     * "spring" from when
     * Prometheus integration didn't exist in Spring itself.
     *
     * @return
     */
    private String job() {
        String job = pushgatewayProperties.getJob();
        if (job == null) {
            job = environment.getProperty("spring.application.name");
        }
        if (job == null) {
            job = "spring";
        }
        return job;
    }

    private String getPort(){
        String job = environment.getProperty("server.port");
        if (job == null) {
            job = "0";
        }
        return job;
    }
}

